/*
 * FXGL - JavaFX Game Library. The MIT License (MIT).
 * Copyright (c) AlmasB (almaslvl@gmail.com).
 * See LICENSE for details.
 */

package com.windea.study.kotlin.fxgl.sample.tutorial

import com.almasb.fxgl.app.*
import com.almasb.fxgl.dsl.*
import com.almasb.fxgl.entity.*
import com.almasb.fxgl.input.*
import javafx.geometry.*
import javafx.scene.input.*
import javafx.scene.paint.*
import javafx.scene.shape.*
import javafx.scene.text.*
import kotlin.collections.set

class PongApp : GameApplication() {
	private lateinit var paddle1: Entity
	private lateinit var paddle2: Entity
	private lateinit var ball: Entity
	
	override fun initSettings(settings: GameSettings) {
		settings.title = "Pong"
	}
	
	override fun initInput() {
		FXGL.getInput().addAction(object : UserAction("Up 1") {
			override fun onAction() {
				paddle1.translateY(-PADDLE_SPEED.toDouble())
			}
		}, KeyCode.W)
		FXGL.getInput().addAction(object : UserAction("Down 1") {
			override fun onAction() {
				paddle1.translateY(PADDLE_SPEED.toDouble())
			}
		}, KeyCode.S)
		FXGL.getInput().addAction(object : UserAction("Up 2") {
			override fun onAction() {
				paddle2.translateY(-PADDLE_SPEED.toDouble())
			}
		}, KeyCode.UP)
		FXGL.getInput().addAction(object : UserAction("Down 2") {
			override fun onAction() {
				paddle2.translateY(PADDLE_SPEED.toDouble())
			}
		}, KeyCode.DOWN)
	}
	
	override fun initGameVars(vars: MutableMap<String, Any>) {
		vars["score1"] = 0
		vars["score2"] = 0
	}
	
	override fun initGame() {
		paddle1 = spawnBat(0.0, FXGL.getAppHeight() / 2 - PADDLE_HEIGHT / 2.toDouble())
		paddle2 = spawnBat(FXGL.getAppWidth() - PADDLE_WIDTH.toDouble(), FXGL.getAppHeight() / 2 - PADDLE_HEIGHT / 2.toDouble())
		ball = spawnBall(FXGL.getAppWidth() / 2 - BALL_SIZE / 2.toDouble(), FXGL.getAppHeight() / 2 - BALL_SIZE / 2.toDouble())
	}
	
	override fun initUI() {
		val textScore1: Text = FXGL.getUIFactory().newText("", Color.BLACK, 22.0)
		val textScore2: Text = FXGL.getUIFactory().newText("", Color.BLACK, 22.0)
		textScore1.translateX = 10.0
		textScore1.translateY = 50.0
		textScore2.translateX = FXGL.getAppWidth() - 30.toDouble()
		textScore2.translateY = 50.0
		textScore1.textProperty().bind(FXGL.getGameState().intProperty("score1").asString())
		textScore2.textProperty().bind(FXGL.getGameState().intProperty("score2").asString())
		FXGL.getGameScene().addUINodes(textScore1, textScore2)
	}
	
	override fun onUpdate(tpf: Double) {
		val velocity: Point2D = ball.getObject("velocity")
		ball.translate(velocity)
		if(ball.x == paddle1.rightX && ball.y < paddle1.bottomY && ball.bottomY > paddle1.y) {
			ball.setProperty("velocity", Point2D(-velocity.x, velocity.y))
		}
		if(ball.rightX == paddle2.x && ball.y < paddle2.bottomY && ball.bottomY > paddle2.y) {
			ball.setProperty("velocity", Point2D(-velocity.x, velocity.y))
		}
		if(ball.x <= 0) {
			FXGL.getGameState().increment("score2", +1)
			resetBall()
		}
		if(ball.rightX >= FXGL.getAppWidth()) {
			FXGL.getGameState().increment("score1", +1)
			resetBall()
		}
		if(ball.y <= 0) {
			ball.y = 0.0
			ball.setProperty("velocity", Point2D(velocity.x, -velocity.y))
		}
		if(ball.bottomY >= FXGL.getAppHeight()) {
			ball.y = FXGL.getAppHeight() - BALL_SIZE.toDouble()
			ball.setProperty("velocity", Point2D(velocity.x, -velocity.y))
		}
	}
	
	private fun spawnBat(x: Double, y: Double): Entity {
		return FXGL.entityBuilder()
			.at(x, y)
			.viewWithBBox(Rectangle(PADDLE_WIDTH.toDouble(), PADDLE_HEIGHT.toDouble()))
			.buildAndAttach()
	}
	
	private fun spawnBall(x: Double, y: Double): Entity {
		return FXGL.entityBuilder()
			.at(x, y)
			.viewWithBBox(Rectangle(BALL_SIZE.toDouble(), BALL_SIZE.toDouble()))
			.with("velocity", Point2D(BALL_SPEED.toDouble(), BALL_SPEED.toDouble()))
			.buildAndAttach()
	}
	
	private fun resetBall() {
		ball.setPosition(FXGL.getAppWidth() / 2 - BALL_SIZE / 2.toDouble(), FXGL.getAppHeight() / 2 - BALL_SIZE / 2.toDouble())
		ball.setProperty("velocity", Point2D(BALL_SPEED.toDouble(), BALL_SPEED.toDouble()))
	}
	
	companion object {
		private const val PADDLE_WIDTH = 30
		private const val PADDLE_HEIGHT = 100
		private const val BALL_SIZE = 20
		private const val PADDLE_SPEED = 5
		private const val BALL_SPEED = 5
	}
}

fun main(args: Array<String>) {
	GameApplication.launch(PongApp::class.java, args)
}
